#include <cstdlib> // malloc()

#include "./ThreadCache.h"
#include "./CentralCache.h"

namespace MemoryPool
{
    ThreadCache &ThreadCache::getInstance()
    {
        static thread_local ThreadCache instance{}; // 注意：线程局部
        return instance;
    }

    ThreadCache::ThreadCache()
    {
        // 初始化空闲链表和大小统计
        this->freeNodes_.fill(nullptr);
        this->freeNodeNums_.fill(0);
    }

    void *ThreadCache::alloc(size_t needSize)
    {
        // 处理0大小的分配请求
        if (needSize == 0)
        {
            needSize = SizeManager::ALIGNMENT_BYTES; // 至少分配一个对齐大小
        }
        else if (needSize > SizeManager::MAX_ALLOC_BYTES)
        {
            // 大对象直接从系统分配
            return malloc(needSize);
        }

        size_t needIndex = SizeManager::sizeToIndex(needSize); // 实际上是本层或下层的rangeIndex，这里屏蔽细节不这样命名

        // 更新空闲链表大小
        // freeListSize_[index]--;

        // 检查线程本地空闲链表
        // 找到，取node
        if (void *resultAddr = this->freeNodes_[needIndex])
        {
            // 将freeList_[index]指向的内存节点的下一个内存节点地址（取决于内存块的实现）
            // 这里实现为链表，就是指向头节点的下一个节点
            // 注意写法：void **得到指向void*的指针即类似result->next，再解引用得到next值，即a = b -> next
            this->freeNodes_[needIndex] = *reinterpret_cast<void **>(resultAddr);

            // OPTIMIZE: 应该是分配后才更新数量
            // TODO: 后续节点数量<=0检查
            --this->freeNodeNums_[needIndex];

            return resultAddr;
        }

        // 没找到，申请CentralCache
        return this->allocFromCentralCache(needIndex);
    }

    void *ThreadCache::allocFromCentralCache(const size_t needIndex)
    {
        // 从中心缓存批量获取内存
        void *startAddr = CentralCache::getInstance().allocRange(needIndex);
        if (!startAddr)
        {
            return nullptr;
        }

        // 取一个返回，其余放入空闲链表
        void *resultAddr = startAddr;
        this->freeNodes_[needIndex] = *reinterpret_cast<void **>(startAddr); // 取start->next放入

        // 更新空闲链表大小
        size_t nodeNum{0};
        void *currAddr = startAddr; // 从startAddr开始遍历

        // 计算从中心缓存获取的内存节点数量
        while (currAddr != nullptr)
        {
            ++nodeNum;
            currAddr = *reinterpret_cast<void **>(currAddr); // 遍历下一个内存节点
        }

        // 更新freeListSize_，增加获取的内存节点数量
        this->freeNodeNums_[needIndex] += nodeNum; // 前面没找到才会调这个函数，实际就是0 + nodeNum

        return resultAddr;
    }

    void ThreadCache::dealloc(void *needStart, const size_t needSize)
    {
        if (needSize > SizeManager::MAX_ALLOC_BYTES)
        {
            free(needStart);
            return;
        }

        size_t needIndex = SizeManager::sizeToIndex(needSize);

        // 插入到线程本地空闲链表
        *reinterpret_cast<void **>(needStart) = this->freeNodes_[needIndex]; // 头插，array[]如果没有一开始是nullptr
        this->freeNodes_[needIndex] = needStart;                             // 修改指针指向

        // 更新空闲链表大小
        ++this->freeNodeNums_[needIndex]; // 增加对应大小的空闲链表大小

        // 判断是否需要将部分内存回收给中心缓存
        if (this->freeNodeNums_[needIndex] > this->deallocThreshold_) // 节点数量大于阈值
        {
            this->deallocToCentralCache(this->freeNodes_[needIndex], needSize, needIndex, this->freeNodeNums_[needIndex]);
        }
    }

    void ThreadCache::deallocToCentralCache(void *needStart, const size_t needSize,
                                            const size_t needIndex, const size_t needNodeNum)
    {
        // OPTIMIZE: 直接传参，不需要再重复计算
        // 根据大小计算对应的索引
        // size_t needIndex = SizeManager::sizeToIndex(needSize);

        // // 计算要归还内存节点数量
        // size_t needNodeNum = this->freeNodeNums_[needIndex];
        // if (needNodeNum <= 1)
        // {
        //     return; // 如果只有一个，则不归还
        // }

        // 保留一部分在ThreadCache中（比如保留1/4）
        size_t keepNodeNum = std::max(needNodeNum / 4, size_t(1));
        size_t returnNodeNum = needNodeNum - keepNodeNum;

        // 使用对齐后的大小计算分割点，得到要保留的
        char *splitNode = static_cast<char *>(needStart);
        for (size_t i = 0; i < keepNodeNum - 1; ++i) // 索引=数量-1
        {
            // 当前节点指向下一个节点，node = node -> next
            // 移动到要保留的尾节点splitNode->next = 要归还的头节点
            splitNode = reinterpret_cast<char *>(*reinterpret_cast<void **>(splitNode));
            if (!splitNode)
            {
                // 如果链表提前结束，更新实际的返回数量
                returnNodeNum = needNodeNum - (i + 1);
                break;
            }
        }

        // 将内存节点串成链表
        if (splitNode)
        {
            // 将要归还的部分和要保留的部分断开，前面splitNode是保留的，后面nextNode是归还的
            void *nextNode = *reinterpret_cast<void **>(splitNode); // 记录下一个节点是要归还的头节点
            *reinterpret_cast<void **>(splitNode) = nullptr;        // 断开连接

            // 更新ThreadCache的空闲链表
            this->freeNodes_[needIndex] = needStart;

            // 更新空闲链表大小
            this->freeNodeNums_[needIndex] = keepNodeNum;

            // 获取对齐后的实际块大小
            size_t alignedSize = SizeManager::roundUp(needSize);
            // 将剩余部分返回给CentralCache
            if (returnNodeNum > 0 && nextNode)
            {
                CentralCache::getInstance().deallocRange(nextNode, needIndex, returnNodeNum * alignedSize);
            }
        }
    }

    // 上面是按需申请一个range/node，下面是批量申请多个同样大小的range/node
    void *ThreadCache::allocFromCentralCache2(const size_t needIndex)
    {
        size_t needSize = (needIndex + 1) * SizeManager::ALIGNMENT_BYTES;

        // 根据对象内存大小计算批量获取的数量
        size_t nodeNum = needSizeToNodeNum(needSize);

        // 从中央缓存批量获取内存
        void *startAddr = CentralCache::getInstance().allocRange2(needIndex, nodeNum);
        if (!startAddr)
        {
            return nullptr;
        }

        // 更新自由链表大小
        this->freeNodeNums_[needIndex] += nodeNum; // 增加对应大小的空闲链表大小

        // 取一个返回，其余放入线程本地空闲链表
        void *resultAddr = startAddr;
        if (nodeNum > 1)
        {
            this->freeNodes_[needIndex] = *reinterpret_cast<void **>(startAddr); // resultAddr = startAddr->next
        }

        return resultAddr;
    }

    size_t ThreadCache::needSizeToNodeNum(const size_t needSize)
    {
        // 基准：每次批量获取不超过4KB内存
        constexpr size_t MAX_SIZE = 4 * 1024; // 4KB

        // 根据对象大小设置合理的基准节点数
        size_t baseNodeNum{0};
        if (needSize <= 32)
            baseNodeNum = 64; // 64 * 32 = 2KB
        else if (needSize <= 64)
            baseNodeNum = 32; // 32 * 64 = 2KB
        else if (needSize <= 128)
            baseNodeNum = 16; // 16 * 128 = 2KB
        else if (needSize <= 256)
            baseNodeNum = 8; // 8 * 256 = 2KB
        else if (needSize <= 512)
            baseNodeNum = 4; // 4 * 512 = 2KB
        else if (needSize <= 1024)
            baseNodeNum = 2; // 2 * 1024 = 2KB
        else
            baseNodeNum = 1; // 大于1024的对象每次只从中心缓存取1个

        // 计算最大节点数
        size_t maxNodeNum = std::max(size_t(1), MAX_SIZE / needSize);

        // 取最小值，但确保至少返回1
        // return std::max(sizeof(1), std::min(maxNum, baseNum));
        return std::max(size_t(1), std::min(baseNodeNum, maxNodeNum));
    }
} // namespace MemoryPool
